Loan data
This data set contains 113,937 loans with 81 variables on each loan, including loan amount, borrower rate (or interest rate), current loan status, borrower income, borrower employment status, borrower credit history, and the latest payment information. The explanation of the variables can be found there: https://www.prosper.com/Downloads/Services/Documentation/ProsperDataExport_Details.html
library(ggplot2)
Paket <U+393C><U+3E31>ggplot2<U+393C><U+3E32> wurde unter R Version 3.3.3 erstellt
loans <- read.csv('prosperloanData.csv')
head(loans)
Loan original amount
ggplot(aes(x = LoanOriginalAmount), data = loans) +
geom_histogram(color = ('#24323e'), fill = ('#02ccba')) +
ggtitle('Histogram of original loan amount')

summary(loans$LoanOriginalAmount)
Min. 1st Qu. Median Mean 3rd Qu. Max.
1000 4000 6500 8337 12000 35000
The median of the loans is 6500. I suggest the money is needed for extra expenses due to unexpected problems, like repairs at home or taking a small loan for a holiday.
Loans by year
# Convert date to year ---------------------------
loans$LoanOriginationYear <- format(as.Date(loans$LoanOriginationDate, format="%Y-%m-%d"),"%Y")
ggplot(aes(x = LoanOriginationYear, y = LoanOriginalAmount, fill = Term,
group = LoanOriginationYear), data = loans) +
geom_bar(aes(group = LoanOriginationYear), position = 'dodge', stat = 'identity')+
ggtitle("Loans by Year") +
labs(x = "Loan origination year", y = "Loan original amount")

In the plot above we can see that from 2006 to 2010 the loans where about 25000 and short term. Then in 2011 people took the same amount of loan, but with a longer term. In 2012 even more people neede long term loans. In 2013 and 2014 people needed higher long term loans.
Home owner
No we want to take a look at the home owners In the next step we want to proof that people who aren t home owners need more often small loans for vacation, home improvement or household expenses than home owners.
summary(loans$IsBorrowerHomeowner)
False True
56459 57478
Our suggestion that house owners need less loans, seem to be wrong. Almost half the amount of borrowers are house owners. Now we want to limit the loan amount to a smaller range, because we want to know if house owners also need smaller loans, for home improvments or others. ###Boxplot homeowner - limited loan original amount
qplot(x = IsBorrowerHomeowner, y = LoanOriginalAmount,
data = loans, geom = 'boxplot') +
scale_y_continuous(limits = c(1000, 20000))

In the plot above we can see that house owners need bigger amounts of money than non house owners.
Next we want to see, if there is a relation between the prosper rate and the fact that the borrower is a house owner. Normally a house owner has a better rating, due to more financial security. ###Boxplot homeowner - Prosper score
qplot(x=IsBorrowerHomeowner, y=ProsperScore,
data=loans, geom = 'boxplot') +
scale_y_continuous()

Another surprise here. The Prosper score is almost the same for both kinds of borrowers.
Current Delinquencies by home owner
summary(loans$MonthlyLoanPayment)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 131.6 217.7 272.5 371.6 2252.0
ggplot(aes(x=CurrentDelinquencies, y=..count../sum(..count..)), data = subset(loans, !is.na(IsBorrowerHomeowner))) +
geom_freqpoly(aes(color = IsBorrowerHomeowner)) +
xlab('CurrentDelinquencies') +
ylab('Percentage of Borrowers with current delinquencies')

In the plot above we can not notice any difference between home owners and non home owners in current delinquencies.
Category of Loan
To get a better readability, we are going to map the numeric values to better readable strings according to this site: https://www.prosper.com/Downloads/Services/Documentation/ProsperDataExport_Details.html
#transform the numeric value of categories to readable names--------------
loans$Category[loans$ListingCategory..numeric. == '0'] <- 'Not Available'
loans$Category[loans$ListingCategory..numeric. == '1'] <- 'Debt Consolidation'
loans$Category[loans$ListingCategory..numeric. == '2'] <- 'Home Improvement'
loans$Category[loans$ListingCategory..numeric. == '3'] <- 'Business'
loans$Category[loans$ListingCategory..numeric. == '4'] <- 'Personal Loan'
loans$Category[loans$ListingCategory..numeric. == '5'] <- 'Student Use'
loans$Category[loans$ListingCategory..numeric. == '6'] <- 'Auto'
loans$Category[loans$ListingCategory..numeric. == '7'] <- 'Other'
loans$Category[loans$ListingCategory..numeric. == '8'] <- 'Baby & Adoption'
loans$Category[loans$ListingCategory..numeric. == '9'] <- 'Boat'
loans$Category[loans$ListingCategory..numeric. == '10'] <- 'Cosmetic Procedure'
loans$Category[loans$ListingCategory..numeric. == '11'] <- 'Engagement Ring'
loans$Category[loans$ListingCategory..numeric. == '12'] <- 'Green Loans'
loans$Category[loans$ListingCategory..numeric. == '13'] <- 'Household Expenses'
loans$Category[loans$ListingCategory..numeric. == '14'] <- 'Large Purchases'
loans$Category[loans$ListingCategory..numeric. == '15'] <- 'Medical/Dental'
loans$Category[loans$ListingCategory..numeric. == '16'] <- 'Motorcycle'
loans$Category[loans$ListingCategory..numeric. == '17'] <- 'RV'
loans$Category[loans$ListingCategory..numeric. == '18'] <- 'Taxes'
loans$Category[loans$ListingCategory..numeric. == '19'] <- 'Vacation'
loans$Category[loans$ListingCategory..numeric. == '20'] <- 'Wedding Loans'
reorder_size <- function(x) {
factor(x, levels = names(sort(table(x))))
}
ggplot(data = subset(loans,
LoanOriginalAmount > 2500
& LoanOriginalAmount <= 9000
& Category != 'Debt Consolidation'
& Category != 'Not Available'
& Category != 'Other'),
aes(reorder_size(Category))) +
geom_bar(colour='#24323e', fill='#02ccba')+
ggtitle("Amount of loans by category") +
theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))+
labs(x="", y = "Number of loans") +
coord_flip()

As the most categories are ‘debt consolidation’, ‘not availabe’ and ‘other’, we cant really tell if it is like suggested. To get a better picture, we excludet those categories in the plot above. The data is not complete. But mostly money is needed for household expenses or home improvement.
Occupations of Borrowers
head(loans$Occupation)
[1] Other Professional Other Skilled Labor Executive
[6] Professional
68 Levels: Accountant/CPA Administrative Assistant Analyst ... Waiter/Waitress
Because there are 68 different types of occupation we are going to combine groups into a new data frame into bigger occupation groups.
#summarize different occupations into grouped occupations----------------
loans$GroupedOccupation <- factor(loans$Occupation)
levels(loans$GroupedOccupation) <- list(
Student=c("Student - College Graduate Student",
"Student - College Senior",
"Student - Community College",
"Student - College Freshman",
"Student - College Junior",
"Student - College Sophomore",
"Student - Technical School"),
Medical_Health=c("Doctor", "Nurse's Aide",
"Nurse (RN)",
"Nurse (LPN)",
"Dentist",
"Pharmacist",
"Medical Technician",
"Psychologist"),
Sales=c("Sales - Commission",
"Sales - Retail",
"Car Dealer",
"Realtor"),
Service=c("Food Service Management",
"Food Service",
"Postal Service",
"Social Worker",
"Truck Driver",
"Bus Driver",
"Retail Management",
"Waiter/Waitress",
"Flight Attendant",
"Clerical",
"Religious",
"Clergy"),
Laborer=c("Construction",
"Laborer",
"Skilled Labor",
"Landscaping",
"Homemaker",
"Fireman",
"Executive",
"Teacher's Aide",
"Computer Programmer",
"Administrative Assistant",
"Professional",
"Accountant/CPA",
"Tradesman - Carpenter",
"Tradesman - Mechanic",
"Tradesman - Electrician",
"Tradesman - Plumber",
"Pilot - Private/Commercial"),
HigherEdJobs=c("Architect",
"Biologist",
"Engineer - Electrical",
"Engineer - Mechanical",
"Engineer - Chemical",
"Judge", "Teacher",
"Scientist",
"Professor",
"Attorney", "Analyst", "Accountant/CPA"
),
CivilService=c("Civil Service",
"Military Officer",
"Police Officer/Correction Officer",
"Military Enlisted"),
Other=c("Other", "")
)
ggplot(data=subset(loans, GroupedOccupation != 'Other' & !is.na(GroupedOccupation)), x=GroupedOccupation, aes(reorder_size(GroupedOccupation))) +
geom_bar(colour='#24323e', fill='#02ccba')+
ggtitle("Borrowers by Occupation")+
labs(x="", y = "Number of loans")+
theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))

Let us take a closer look, how much money is needed, depending on the fact that a borrower is /is not a home owner and his occupational group.
Loan amount by grouped occupation and homeowner status
ggplot(aes(x = IsBorrowerHomeowner, y = LoanOriginalAmount),
data = loans) +
stat_summary(fun.y = mean, geom = 'point', shape = 4)

ggplot(aes(x = GroupedOccupation, y = LoanOriginalAmount),
data = loans) +
theme(axis.text.x = element_text(angle=45,hjust=0.5,vjust=0.5))+
geom_point(aes(color = IsBorrowerHomeowner))

In the plot above, we can see that stundents need the smaller amounts of money. The higher the loan gets, the more homeowners are the borrowers.
To get better information, we want to limit the loan to a maximum of 5000. ###Limited loan amount by grouped occupation and homeowner status
ggplot(aes(x = GroupedOccupation, y = LoanOriginalAmount),
data = loans) +
theme(axis.text.x = element_text(angle = 45,hjust = 0.5,vjust = 0.5))+
geom_point(aes(color = IsBorrowerHomeowner))+
scale_y_continuous(limits = c(1000, 5000))

For money less than 5000 the majority of the borrowers are not home owners. Although in some occupational groups there are a lot of home owners. This may be caused by our occupational grouping, which contains professions with a wide income range. For example in the group ‘Medical_Health’, there are doctors and nurses etc.
Prosper rating
#-----------------create a new DF with info about the prosper rating and the amount
library(dplyr)
Attache Paket: <U+393C><U+3E31>dplyr<U+393C><U+3E32>
The following objects are masked from <U+393C><U+3E31>package:stats<U+393C><U+3E32>:
filter, lag
The following objects are masked from <U+393C><U+3E31>package:base<U+393C><U+3E32>:
intersect, setdiff, setequal, union
creditsByGrade <- group_by(loans, ProsperRating..numeric.)
creditsByGrade <- summarise(creditsByGrade,
mean_amount = mean(LoanOriginalAmount),
median_amount = median(LoanOriginalAmount),
min_amount = min(LoanOriginalAmount),
max_amount = max(LoanOriginalAmount),
n = n())
creditsByGrade
Borrower Rate - Prosper Score
ggplot(aes(x=BorrowerRate, y=ProsperScore), data = loans)+
geom_line()+
geom_smooth()+
ggtitle("Line Plot of borrower rate and prosper score")

No surprises here, the better the Prosper Score, the better the borrower rate.
Prosper rating if the income is verifiable
We suggest that the prosper rating is better if the income is verifiable.
ggplot(aes(x = ProsperRating..numeric., y = ..count../sum(..count..)), data = subset(loans, !is.na(IncomeVerifiable))) +
geom_freqpoly(aes(color=IncomeVerifiable)) +
xlab('Prosper Rating') +
ylab('Percentage of Borrowers with that Prosper Rating')+
ggtitle("Prosper rating in percent by verifiable income")

Yes, our suggestion is right.
Employment status duration
Next we want to take a look at the credit grade and the employment status duration. We suggest the longer the employment status, the better is the credit grade.
summary(loans$EmploymentStatusDuration)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.00 26.00 67.00 96.07 137.00 755.00 7625
ggplot(aes(x = CreditGrade, y = EmploymentStatusDuration),
data = loans) +
geom_point(aes(color =
EmploymentStatus))+
ggtitle("Credit grade by employment status duration")

As we can see in the plot above, most of the loan takers are full-time employees. But also there is a lot of Data missing. To get a clearer picture, we want do save the employment status duration into buckets.
# save employment status duration into buckets-----------------------------
loans$bucket_EmploymentStatusDuration <- cut(loans$EmploymentStatusDuration,
c(0, 12, 24, 36, 48, 50, 62, 74, 86, 98, 110, 122, 134, 146, 755))
ggplot(aes(x = CreditGrade, y = bucket_EmploymentStatusDuration), data = loans) +
geom_point(aes(color = EmploymentStatus))+
ggtitle("Credit grade by employment status duration in buckets")

This plot is not much better, altough it is easier to read. We can see that there is data missing and that most of the loan takers are full time employees.
ggplot(aes(x = LoanOriginalAmount, fill = LoanStatus), data = loans) +
facet_wrap(~Term) +
geom_histogram(aes(color = LoanStatus)) +
scale_fill_brewer(type = 'qual') +
ggtitle("Histogram of loan Amounts by status and terms")

In the plot above we can see that most of the loans are mid-term.
Debt to income ratio
ggplot(data = loans, aes(x = DebtToIncomeRatio)) +
geom_histogram(colour = '#24323e', fill = '#02ccba', binwidth = 0.005) +
xlim(0, quantile(loans$DebtToIncomeRatio, prob = 0.5, na.rm = TRUE)) +
ggtitle("Debt To Income Ratio") +
xlab("Debt to Income Ratio") +
ylab("Count")

summary(loans$loan_income_ratio)
Length Class Mode
0 NULL NULL
Lender yield
We suggest that there is a higher lender yield, if the borrower rate is higher.
library(dplyr)
ggplot(loans, aes(x = LenderYield, y = BorrowerRate)) +
geom_point(alpha = 1/20, colour = '#02ccba') +
scale_x_continuous(limits = c(0, quantile(loans$LenderYield, 0.75))) +
scale_y_continuous(
limits = c(0 , quantile(loans$BorrowerRate, 0.75))) +
ggtitle('Lender yield and borrower rate')

Yes, we can see clearly the higher the borrower rate, the higher the lender yield.
Lender yield by number of Investors
ggplot(aes(x = BorrowerRate, y = Investors), data=loans) +
geom_point(aes(color=LenderYield))+
ylim(0, 600)+
ggtitle('Lender yield by number of investors')

In the plot above we can see quite well, that the investors yield gets higher the higher the borrower rate gets.
ggplot(aes(x = loans$Investors, y = ..count..), data = loans) +
geom_freqpoly(aes(color = Investors), binwidth=0.1) +
scale_x_continuous(limits = c(0, 250), breaks = seq(0, 50, 250)) +
scale_y_continuous(breaks = seq(0, 750, 50))+
ylim(0, 750)+
xlim(1, 250)+
xlab('number of Investors') +
ylab('count') +
ggtitle('Number of Investors per Loan')
Scale for 'y' is already present. Adding another scale for 'y', which will
replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will
replace the existing scale.

The majority of the Investors is just one person. In the plot above, we limit the number of loans given to 1500 in order to get a better picture of loans given by more then one investor.
Current delinquencies by credit grade
qplot(x=CreditGrade, y=DelinquenciesLast7Years,
data=loans, geom='boxplot')+
ylim(0, 25)

As we can see in the plot above, the better the credit grade, less delinquenices.
Loan amount by term, grouped by grouped occupation and category
ggplot(aes(x = Term, y = LoanOriginalAmount), data = loans) +
facet_wrap(~GroupedOccupation) +
geom_point(aes(color = Category)) +
scale_y_continuous(limits = c(1000, 5000))
The plot above gives us a nice overview. As we can see most of the loans are mid term loans with a duration of 36 month. The usages of the loans are well mixed.
ggplot(aes(EstimatedReturn, EstimatedLoss),
data=subset(loans, GroupedOccupation != 'Other'
& !is.na(GroupedOccupation)
& !is.na(IncomeRange)
& IncomeRange != 'Not displayed'
& IncomeRange != 'Not employed'))+
geom_point(aes(size=IncomeRange, colour=GroupedOccupation))+
ggtitle('Estimated loss and estimated return by income range of borrower')

The plot above gives an overview of the estimated return and estimated loss by grouped occupation and income range. We can see that there is a small group where the estimated loss is high and there is not an estimated return. But we can’t see that this is happening only to a special occuption group or income range in there. Most of the estimated returns and estimated losses are between 0 and 0.1.
Because the plot is hard to read due to the density of information, we will take a closer look at on occupation group - the students.
ggplot(aes(EstimatedReturn, EstimatedLoss),
data=subset(loans, GroupedOccupation == 'Student'
& IncomeRange != 'Not displayed'
& IncomeRange != 'Not employed'))+
geom_point(aes(size=IncomeRange, colour=IncomeRange))+
ggtitle('Estimated loss and estimated return by income range for students')

This plot shows that there are some students that seem to earn already a lot of money, altought the majority earns between $1 - 24999.
ggplot(loans, aes(LP_CustomerPayments, LP_InterestandFees)) +
geom_point(aes(colour =LP_ServiceFees), size = 1) +
coord_equal()

This plot shows that the lower the customer payments are the lower the service fees and interest fees are.
Heatmap
ggplot(data = loans, aes(x = Category, y = GroupedOccupation)) +
geom_tile(aes(fill = LoanOriginalAmount)) +
theme(axis.text.x=element_text(angle=60,hjust=1,vjust=0.9))

In the plot above we get a nice overview of the category of loans and the grouped occupations.
LS0tDQp0aXRsZTogIkRBNCBFREEgTE9BTiBEQVRBIiANCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoJ2dncGxvdDInKQ0KYGBgDQojI0xvYW4gZGF0YQ0KVGhpcyBkYXRhIHNldCBjb250YWlucyAxMTMsOTM3IGxvYW5zIHdpdGggODEgdmFyaWFibGVzIG9uIGVhY2ggbG9hbiwgaW5jbHVkaW5nIGxvYW4gYW1vdW50LCBib3Jyb3dlciByYXRlIChvciBpbnRlcmVzdCByYXRlKSwgY3VycmVudCBsb2FuIHN0YXR1cywgYm9ycm93ZXIgaW5jb21lLCBib3Jyb3dlciBlbXBsb3ltZW50IHN0YXR1cywgYm9ycm93ZXIgY3JlZGl0IGhpc3RvcnksIGFuZCB0aGUgbGF0ZXN0IHBheW1lbnQgaW5mb3JtYXRpb24uDQpUaGUgZXhwbGFuYXRpb24gb2YgdGhlIHZhcmlhYmxlcyBjYW4gYmUgZm91bmQgdGhlcmU6DQpodHRwczovL3d3dy5wcm9zcGVyLmNvbS9Eb3dubG9hZHMvU2VydmljZXMvRG9jdW1lbnRhdGlvbi9Qcm9zcGVyRGF0YUV4cG9ydF9EZXRhaWxzLmh0bWwNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbG9hbnMgPC0gcmVhZC5jc3YoJ3Byb3NwZXJsb2FuRGF0YS5jc3YnKQ0KaGVhZChsb2FucykNCmBgYA0KDQojIyNMb2FuIG9yaWdpbmFsIGFtb3VudA0KYGBge3IgMSAtIEhvdyBtdWNoIG1vbmV5IGlzIG5lZWRlZH0NCmdncGxvdChhZXMoeCA9IExvYW5PcmlnaW5hbEFtb3VudCksIGRhdGEgPSBsb2FucykgKyANCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAoJyMyNDMyM2UnKSwgZmlsbCA9ICgnIzAyY2NiYScpKSArDQogIGdndGl0bGUoJ0hpc3RvZ3JhbSBvZiBvcmlnaW5hbCBsb2FuIGFtb3VudCcpDQoNCnN1bW1hcnkobG9hbnMkTG9hbk9yaWdpbmFsQW1vdW50KQ0KYGBgDQpUaGUgbWVkaWFuIG9mIHRoZSBsb2FucyBpcyA2NTAwLiBJIHN1Z2dlc3QgdGhlIG1vbmV5IGlzIG5lZWRlZCBmb3IgZXh0cmEgZXhwZW5zZXMgZHVlIHRvIHVuZXhwZWN0ZWQgcHJvYmxlbXMsIGxpa2UgcmVwYWlycyBhdCBob21lIG9yIHRha2luZyBhIHNtYWxsIGxvYW4gZm9yIGEgaG9saWRheS4NCg0KIyMjTG9hbnMgYnkgeWVhcg0KYGBge3IgMiAtIExvYW5zIG92ZXIgdGltZSBieSB0ZXJtfQ0KIyBDb252ZXJ0IGRhdGUgdG8geWVhciAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCmxvYW5zJExvYW5PcmlnaW5hdGlvblllYXIgPC0gZm9ybWF0KGFzLkRhdGUobG9hbnMkTG9hbk9yaWdpbmF0aW9uRGF0ZSwgZm9ybWF0PSIlWS0lbS0lZCIpLCIlWSIpDQoNCmdncGxvdChhZXMoeCA9IExvYW5PcmlnaW5hdGlvblllYXIsIHkgPSBMb2FuT3JpZ2luYWxBbW91bnQsIGZpbGwgPSBUZXJtLA0KICAgICAgICAgICBncm91cCA9IExvYW5PcmlnaW5hdGlvblllYXIpLCBkYXRhID0gbG9hbnMpICsNCiAgICBnZW9tX2JhcihhZXMoZ3JvdXAgPSBMb2FuT3JpZ2luYXRpb25ZZWFyKSwgcG9zaXRpb24gPSAnZG9kZ2UnLCBzdGF0ID0gJ2lkZW50aXR5JykrDQogICAgZ2d0aXRsZSgiTG9hbnMgYnkgWWVhciIpICsNCiAgICBsYWJzKHggPSAiTG9hbiBvcmlnaW5hdGlvbiB5ZWFyIiwgeSA9ICJMb2FuIG9yaWdpbmFsIGFtb3VudCIpDQpgYGANCkluIHRoZSBwbG90IGFib3ZlIHdlIGNhbiBzZWUgdGhhdCBmcm9tIDIwMDYgdG8gMjAxMCB0aGUgbG9hbnMgd2hlcmUgYWJvdXQgMjUwMDAgYW5kIHNob3J0IHRlcm0uIFRoZW4gaW4gMjAxMSBwZW9wbGUgdG9vayB0aGUgc2FtZSBhbW91bnQgb2YgbG9hbiwgYnV0IHdpdGggYSBsb25nZXIgdGVybS4gSW4gMjAxMiBldmVuIG1vcmUgcGVvcGxlIG5lZWRlIGxvbmcgdGVybSBsb2Fucy4gSW4gMjAxMyBhbmQgMjAxNCBwZW9wbGUgbmVlZGVkIGhpZ2hlciBsb25nIHRlcm0gbG9hbnMuIA0KDQojIyNIb21lIG93bmVyDQpObyB3ZSB3YW50IHRvIHRha2UgYSBsb29rIGF0IHRoZSBob21lIG93bmVycw0KSW4gdGhlIG5leHQgc3RlcCB3ZSB3YW50IHRvIHByb29mIHRoYXQgcGVvcGxlIHdobyBhcmVuIHQgaG9tZSBvd25lcnMgbmVlZCBtb3JlIG9mdGVuIHNtYWxsIGxvYW5zIGZvciB2YWNhdGlvbiwgaG9tZSBpbXByb3ZlbWVudCBvciBob3VzZWhvbGQgZXhwZW5zZXMgdGhhbiBob21lIG93bmVycy4NCmBgYHtyfQ0Kc3VtbWFyeShsb2FucyRJc0JvcnJvd2VySG9tZW93bmVyKQ0KYGBgDQoNCk91ciBzdWdnZXN0aW9uIHRoYXQgaG91c2Ugb3duZXJzIG5lZWQgbGVzcyBsb2Fucywgc2VlbSB0byBiZSB3cm9uZy4gQWxtb3N0IGhhbGYgdGhlIGFtb3VudCBvZiBib3Jyb3dlcnMgYXJlIGhvdXNlIG93bmVycy4NCk5vdyB3ZSB3YW50IHRvIGxpbWl0IHRoZSBsb2FuIGFtb3VudCB0byBhIHNtYWxsZXIgcmFuZ2UsIGJlY2F1c2Ugd2Ugd2FudCB0byBrbm93IGlmIGhvdXNlIG93bmVycyBhbHNvIG5lZWQgc21hbGxlciBsb2FucywgZm9yIGhvbWUgaW1wcm92bWVudHMgb3Igb3RoZXJzLg0KIyMjQm94cGxvdCBob21lb3duZXIgLSAgbGltaXRlZCBsb2FuIG9yaWdpbmFsIGFtb3VudA0KYGBge3IgMyAtIFJlbGF0aW9uIGJldHdlZW4gcHJvc3BlciBzY29yZSBhbmQgaG9tZW93bmVyfQ0KICBxcGxvdCh4ID0gSXNCb3Jyb3dlckhvbWVvd25lciwgeSA9IExvYW5PcmlnaW5hbEFtb3VudCwNCiAgICAgICAgZGF0YSA9IGxvYW5zLCBnZW9tID0gJ2JveHBsb3QnKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMTAwMCwgMjAwMDApKQ0KYGBgDQpJbiB0aGUgcGxvdCBhYm92ZSB3ZSBjYW4gc2VlIHRoYXQgaG91c2Ugb3duZXJzIG5lZWQgYmlnZ2VyIGFtb3VudHMgb2YgbW9uZXkgdGhhbiBub24gaG91c2Ugb3duZXJzLg0KDQpOZXh0IHdlIHdhbnQgdG8gc2VlLCBpZiB0aGVyZSBpcyBhIHJlbGF0aW9uIGJldHdlZW4gdGhlIHByb3NwZXIgcmF0ZSBhbmQgdGhlIGZhY3QgdGhhdCB0aGUgYm9ycm93ZXIgaXMgYSBob3VzZSBvd25lci4gTm9ybWFsbHkgYSBob3VzZSBvd25lciBoYXMgYSBiZXR0ZXIgcmF0aW5nLCBkdWUgdG8gbW9yZSBmaW5hbmNpYWwgc2VjdXJpdHkuDQojIyNCb3hwbG90IGhvbWVvd25lciAtIFByb3NwZXIgc2NvcmUNCmBgYHtyIDQgLSByZWxhdGlvbiBiZXR3ZWVuIHByb3NwZXIgc2NvcmUgYW5kIGhvbWV3bmVyfQ0KcXBsb3QoeD1Jc0JvcnJvd2VySG9tZW93bmVyLCB5PVByb3NwZXJTY29yZSwNCiAgICAgICAgZGF0YT1sb2FucywgZ2VvbSA9ICdib3hwbG90JykgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoKQ0KYGBgDQpBbm90aGVyIHN1cnByaXNlIGhlcmUuIFRoZSBQcm9zcGVyIHNjb3JlIGlzIGFsbW9zdCB0aGUgc2FtZSBmb3IgYm90aCBraW5kcyBvZiBib3Jyb3dlcnMuDQoNCiMjI0N1cnJlbnQgRGVsaW5xdWVuY2llcyBieSBob21lIG93bmVyDQpgYGB7ciA1IC0gY3VycmVudCBkZWxpbnF1ZW5jaWVzIGlmIGhvbWVvd25lciB9DQpzdW1tYXJ5KGxvYW5zJE1vbnRobHlMb2FuUGF5bWVudCkNCmdncGxvdChhZXMoeD1DdXJyZW50RGVsaW5xdWVuY2llcywgeT0uLmNvdW50Li4vc3VtKC4uY291bnQuLikpLCBkYXRhID0gc3Vic2V0KGxvYW5zLCAhaXMubmEoSXNCb3Jyb3dlckhvbWVvd25lcikpKSArDQogIGdlb21fZnJlcXBvbHkoYWVzKGNvbG9yID0gSXNCb3Jyb3dlckhvbWVvd25lcikpICsgDQogIHhsYWIoJ0N1cnJlbnREZWxpbnF1ZW5jaWVzJykgKyANCiAgeWxhYignUGVyY2VudGFnZSBvZiBCb3Jyb3dlcnMgd2l0aCBjdXJyZW50IGRlbGlucXVlbmNpZXMnKQ0KYGBgDQpJbiB0aGUgcGxvdCBhYm92ZSB3ZSBjYW4gbm90IG5vdGljZSBhbnkgZGlmZmVyZW5jZSBiZXR3ZWVuIGhvbWUgb3duZXJzIGFuZCBub24gaG9tZSBvd25lcnMgaW4gY3VycmVudCBkZWxpbnF1ZW5jaWVzLg0KDQojIyNDYXRlZ29yeSBvZiBMb2FuDQpUbyBnZXQgYSBiZXR0ZXIgcmVhZGFiaWxpdHksIHdlIGFyZSBnb2luZyB0byBtYXAgdGhlIG51bWVyaWMgdmFsdWVzIHRvIGJldHRlciByZWFkYWJsZSBzdHJpbmdzIGFjY29yZGluZyB0byB0aGlzIHNpdGU6IGh0dHBzOi8vd3d3LnByb3NwZXIuY29tL0Rvd25sb2Fkcy9TZXJ2aWNlcy9Eb2N1bWVudGF0aW9uL1Byb3NwZXJEYXRhRXhwb3J0X0RldGFpbHMuaHRtbA0KDQpgYGB7ciBsb2FucyBieSBjYXRlZ29yeSB9DQojdHJhbnNmb3JtIHRoZSBudW1lcmljIHZhbHVlIG9mIGNhdGVnb3JpZXMgdG8gcmVhZGFibGUgbmFtZXMtLS0tLS0tLS0tLS0tLQ0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMCddIDwtICdOb3QgQXZhaWxhYmxlJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMSddIDwtICdEZWJ0IENvbnNvbGlkYXRpb24nDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcyJ10gPC0gJ0hvbWUgSW1wcm92ZW1lbnQnDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICczJ10gPC0gJ0J1c2luZXNzJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnNCddIDwtICdQZXJzb25hbCBMb2FuJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnNSddIDwtICdTdHVkZW50IFVzZScNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzYnXSA8LSAnQXV0bycNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzcnXSA8LSAnT3RoZXInDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICc4J10gPC0gJ0JhYnkgJiBBZG9wdGlvbicNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzknXSA8LSAnQm9hdCcNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzEwJ10gPC0gJ0Nvc21ldGljIFByb2NlZHVyZScNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzExJ10gPC0gJ0VuZ2FnZW1lbnQgUmluZycNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzEyJ10gPC0gJ0dyZWVuIExvYW5zJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTMnXSA8LSAnSG91c2Vob2xkIEV4cGVuc2VzJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTQnXSA8LSAnTGFyZ2UgUHVyY2hhc2VzJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTUnXSA8LSAnTWVkaWNhbC9EZW50YWwnDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxNiddIDwtICdNb3RvcmN5Y2xlJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTcnXSA8LSAnUlYnDQpsb2FucyRDYXRlZ29yeVtsb2FucyRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxOCddIDwtICdUYXhlcycNCmxvYW5zJENhdGVnb3J5W2xvYW5zJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzE5J10gPC0gJ1ZhY2F0aW9uJw0KbG9hbnMkQ2F0ZWdvcnlbbG9hbnMkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMjAnXSA8LSAnV2VkZGluZyBMb2FucycNCmBgYA0KDQoNCmBgYHtyIDYgLSBXaHkgaXMgdGhlIG1vbmV5IG5lZWRlZH0NCnJlb3JkZXJfc2l6ZSA8LSBmdW5jdGlvbih4KSB7DQogIGZhY3Rvcih4LCBsZXZlbHMgPSBuYW1lcyhzb3J0KHRhYmxlKHgpKSkpDQp9DQoNCmdncGxvdChkYXRhID0gc3Vic2V0KGxvYW5zLA0KICAgICAgICAgICAgICAgICAgICBMb2FuT3JpZ2luYWxBbW91bnQgPiAyNTAwDQogICAgICAgICAgICAgICAgICAgICYgTG9hbk9yaWdpbmFsQW1vdW50IDw9IDkwMDANCiAgICAgICAgICAgICAgICAgICAgJiBDYXRlZ29yeSAhPSAnRGVidCBDb25zb2xpZGF0aW9uJw0KICAgICAgICAgICAgICAgICAgICAmIENhdGVnb3J5ICE9ICdOb3QgQXZhaWxhYmxlJw0KICAgICAgICAgICAgICAgICAgICAmIENhdGVnb3J5ICE9ICdPdGhlcicpLA0KICAgICAgIGFlcyhyZW9yZGVyX3NpemUoQ2F0ZWdvcnkpKSkgKw0KICAgICAgZ2VvbV9iYXIoY29sb3VyPScjMjQzMjNlJywgZmlsbD0nIzAyY2NiYScpKw0KICAgICAgZ2d0aXRsZSgiQW1vdW50IG9mIGxvYW5zIGJ5IGNhdGVnb3J5IikgKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEsdmp1c3Q9MC41KSkrDQogICAgICBsYWJzKHg9IiIsIHkgPSAiTnVtYmVyIG9mIGxvYW5zIikgKw0KICAgICAgY29vcmRfZmxpcCgpDQpgYGANCkFzIHRoZSBtb3N0IGNhdGVnb3JpZXMgYXJlICdkZWJ0IGNvbnNvbGlkYXRpb24nLCAnbm90IGF2YWlsYWJlJyBhbmQgJ290aGVyJywgd2UgY2FudCByZWFsbHkgdGVsbCBpZiBpdCBpcyBsaWtlIHN1Z2dlc3RlZC4gVG8gZ2V0IGEgYmV0dGVyIHBpY3R1cmUsIHdlIGV4Y2x1ZGV0IHRob3NlIGNhdGVnb3JpZXMgaW4gdGhlIHBsb3QgYWJvdmUuIFRoZSBkYXRhIGlzIG5vdCBjb21wbGV0ZS4gQnV0IG1vc3RseSBtb25leSBpcyBuZWVkZWQgZm9yIGhvdXNlaG9sZCBleHBlbnNlcyBvciBob21lIGltcHJvdmVtZW50LiANCg0KIyMjT2NjdXBhdGlvbnMgb2YgQm9ycm93ZXJzDQpgYGB7cn0NCmhlYWQobG9hbnMkT2NjdXBhdGlvbikNCmBgYA0KQmVjYXVzZSB0aGVyZSBhcmUgNjggZGlmZmVyZW50IHR5cGVzIG9mIG9jY3VwYXRpb24gd2UgYXJlIGdvaW5nIHRvIGNvbWJpbmUgZ3JvdXBzIGludG8gYSBuZXcgZGF0YSBmcmFtZSBpbnRvIGJpZ2dlciBvY2N1cGF0aW9uIGdyb3Vwcy4NCmBgYHtyIDcgLSBXaG8gbmVlZGVzIHRoZSBtb25leX0NCiNzdW1tYXJpemUgZGlmZmVyZW50IG9jY3VwYXRpb25zIGludG8gZ3JvdXBlZCBvY2N1cGF0aW9ucy0tLS0tLS0tLS0tLS0tLS0NCmxvYW5zJEdyb3VwZWRPY2N1cGF0aW9uIDwtIGZhY3Rvcihsb2FucyRPY2N1cGF0aW9uKQ0KbGV2ZWxzKGxvYW5zJEdyb3VwZWRPY2N1cGF0aW9uKSA8LSBsaXN0KA0KICBTdHVkZW50PWMoIlN0dWRlbnQgLSBDb2xsZWdlIEdyYWR1YXRlIFN0dWRlbnQiLA0KIlN0dWRlbnQgLSBDb2xsZWdlIFNlbmlvciIsIA0KIlN0dWRlbnQgLSBDb21tdW5pdHkgQ29sbGVnZSIsDQoiU3R1ZGVudCAtIENvbGxlZ2UgRnJlc2htYW4iLA0KIlN0dWRlbnQgLSBDb2xsZWdlIEp1bmlvciIsDQoiU3R1ZGVudCAtIENvbGxlZ2UgU29waG9tb3JlIiwNCiJTdHVkZW50IC0gVGVjaG5pY2FsIFNjaG9vbCIpLCANCk1lZGljYWxfSGVhbHRoPWMoIkRvY3RvciIsICJOdXJzZSdzIEFpZGUiLA0KICAgICAgICAgICAgICAgICAiTnVyc2UgKFJOKSIsDQogICAgICAgICAgICAgICAgICJOdXJzZSAoTFBOKSIsDQogICAgICAgICAgICAgICAgICJEZW50aXN0IiwNCiAgICAgICAgICAgICAgICAgIlBoYXJtYWNpc3QiLA0KICAgICAgICAgICAgICAgICAiTWVkaWNhbCBUZWNobmljaWFuIiwNCiAgICAgICAgICAgICAgICAgIlBzeWNob2xvZ2lzdCIpLA0KU2FsZXM9YygiU2FsZXMgLSBDb21taXNzaW9uIiwNCiAgICAgICAgIlNhbGVzIC0gUmV0YWlsIiwNCiAgICAgICAgIkNhciBEZWFsZXIiLA0KICAgICAgICAiUmVhbHRvciIpLA0KU2VydmljZT1jKCJGb29kIFNlcnZpY2UgTWFuYWdlbWVudCIsDQogICAgICAgICAgIkZvb2QgU2VydmljZSIsDQogICAgICAgICAgIlBvc3RhbCBTZXJ2aWNlIiwNCiAgICAgICAgICAiU29jaWFsIFdvcmtlciIsDQogICAgICAgICAgIlRydWNrIERyaXZlciIsDQogICAgICAgICAgIkJ1cyBEcml2ZXIiLA0KICAgICAgICAgICJSZXRhaWwgTWFuYWdlbWVudCIsDQogICAgICAgICAgIldhaXRlci9XYWl0cmVzcyIsDQogICAgICAgICAgIkZsaWdodCBBdHRlbmRhbnQiLA0KICAgICAgICAgICJDbGVyaWNhbCIsDQogICAgICAgICAgIlJlbGlnaW91cyIsDQogICAgICAgICAgIkNsZXJneSIpLA0KTGFib3Jlcj1jKCJDb25zdHJ1Y3Rpb24iLA0KICAgICAgICAgICJMYWJvcmVyIiwNCiAgICAgICAgICAiU2tpbGxlZCBMYWJvciIsDQogICAgICAgICAgIkxhbmRzY2FwaW5nIiwNCiAgICAgICAgICAiSG9tZW1ha2VyIiwNCiAgICAgICAgICAiRmlyZW1hbiIsDQogICAgICAgICAgIkV4ZWN1dGl2ZSIsDQogICAgICAgICAgIlRlYWNoZXIncyBBaWRlIiwNCiAgICAgICAgICAiQ29tcHV0ZXIgUHJvZ3JhbW1lciIsDQogICAgICAgICAgIkFkbWluaXN0cmF0aXZlIEFzc2lzdGFudCIsDQogICAgICAgICAgIlByb2Zlc3Npb25hbCIsDQogICAgICAgICAgIkFjY291bnRhbnQvQ1BBIiwNCiAgICAgICAgICAiVHJhZGVzbWFuIC0gQ2FycGVudGVyIiwNCiAgICAgICAgICAgICJUcmFkZXNtYW4gLSBNZWNoYW5pYyIsDQogICAgICAgICAgICAiVHJhZGVzbWFuIC0gRWxlY3RyaWNpYW4iLA0KICAgICAgICAgICAgIlRyYWRlc21hbiAtIFBsdW1iZXIiLA0KICAgICAgICAgICJQaWxvdCAtIFByaXZhdGUvQ29tbWVyY2lhbCIpLA0KSGlnaGVyRWRKb2JzPWMoIkFyY2hpdGVjdCIsDQogICAgICAgICAgICAgICAiQmlvbG9naXN0IiwNCiAgICAgICAgICAgICAgICJFbmdpbmVlciAtIEVsZWN0cmljYWwiLA0KICAgICAgICAgICAgICAgIkVuZ2luZWVyIC0gTWVjaGFuaWNhbCIsDQogICAgICAgICAgICAgICAiRW5naW5lZXIgLSBDaGVtaWNhbCIsDQogICAgICAgICAgICAgICAiSnVkZ2UiLCAiVGVhY2hlciIsDQogICAgICAgICAgICAgICAiU2NpZW50aXN0IiwNCiAgICAgICAgICAgICAgICJQcm9mZXNzb3IiLA0KICAgICAgICAgICAgICAgIkF0dG9ybmV5IiwgIkFuYWx5c3QiLCAiQWNjb3VudGFudC9DUEEiDQogICAgICAgICAgICAgICApLA0KQ2l2aWxTZXJ2aWNlPWMoIkNpdmlsIFNlcnZpY2UiLA0KICAgICAgICAgICAgICAgIk1pbGl0YXJ5IE9mZmljZXIiLA0KICAgICAgICAgICAgICAgIlBvbGljZSBPZmZpY2VyL0NvcnJlY3Rpb24gT2ZmaWNlciIsDQogICAgICAgICAgICAgICAiTWlsaXRhcnkgRW5saXN0ZWQiKSwNCk90aGVyPWMoIk90aGVyIiwgIiIpDQopDQoNCmdncGxvdChkYXRhPXN1YnNldChsb2FucywgR3JvdXBlZE9jY3VwYXRpb24gIT0gJ090aGVyJyAmICFpcy5uYShHcm91cGVkT2NjdXBhdGlvbikpLCB4PUdyb3VwZWRPY2N1cGF0aW9uLCBhZXMocmVvcmRlcl9zaXplKEdyb3VwZWRPY2N1cGF0aW9uKSkpICsNCiAgICBnZW9tX2Jhcihjb2xvdXI9JyMyNDMyM2UnLCBmaWxsPScjMDJjY2JhJykrDQogICAgZ2d0aXRsZSgiQm9ycm93ZXJzIGJ5IE9jY3VwYXRpb24iKSsNCiAgICBsYWJzKHg9IiIsIHkgPSAiTnVtYmVyIG9mIGxvYW5zIikrDQogICAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEsdmp1c3Q9MC41KSkNCmBgYA0KDQpMZXQgdXMgdGFrZSBhIGNsb3NlciBsb29rLCBob3cgbXVjaCBtb25leSBpcyBuZWVkZWQsIGRlcGVuZGluZyBvbiB0aGUgZmFjdCB0aGF0IGEgYm9ycm93ZXIgaXMgL2lzIG5vdCBhIGhvbWUgb3duZXIgYW5kIGhpcyBvY2N1cGF0aW9uYWwgZ3JvdXAuDQoNCiMjI0xvYW4gYW1vdW50IGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaG9tZW93bmVyIHN0YXR1cw0KYGBge3IgOCAtIExvYW4gYW1vdW50IGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaG9tZW93bmVyIHN0YXR1c30NCmdncGxvdChhZXMoeCA9IElzQm9ycm93ZXJIb21lb3duZXIsIHkgPSBMb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgICAgZGF0YSA9IGxvYW5zKSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tID0gJ3BvaW50Jywgc2hhcGUgPSA0KQ0KDQpnZ3Bsb3QoYWVzKHggPSBHcm91cGVkT2NjdXBhdGlvbiwgeSA9IExvYW5PcmlnaW5hbEFtb3VudCksDQogICAgICBkYXRhID0gbG9hbnMpICsNCiAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTAuNSx2anVzdD0wLjUpKSsNCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gSXNCb3Jyb3dlckhvbWVvd25lcikpDQpgYGANCkluIHRoZSBwbG90IGFib3ZlLCB3ZSBjYW4gc2VlIHRoYXQgc3R1bmRlbnRzIG5lZWQgdGhlIHNtYWxsZXIgYW1vdW50cyBvZiBtb25leS4gVGhlIGhpZ2hlciB0aGUgbG9hbiBnZXRzLCB0aGUgbW9yZSBob21lb3duZXJzIGFyZSB0aGUgYm9ycm93ZXJzLg0KDQpUbyBnZXQgYmV0dGVyIGluZm9ybWF0aW9uLCB3ZSB3YW50IHRvIGxpbWl0IHRoZSBsb2FuIHRvIGEgbWF4aW11bSBvZiA1MDAwLg0KIyMjTGltaXRlZCBsb2FuIGFtb3VudCBieSBncm91cGVkIG9jY3VwYXRpb24gYW5kIGhvbWVvd25lciBzdGF0dXMNCmBgYHtyIDkgLSBMaW1pdGVkIGxvYW4gYW1vdW50IGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaG9tZW93bmVyIHN0YXR1c30NCmdncGxvdChhZXMoeCA9IEdyb3VwZWRPY2N1cGF0aW9uLCB5ID0gTG9hbk9yaWdpbmFsQW1vdW50KSwNCiAgICAgIGRhdGEgPSBsb2FucykgKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSxoanVzdCA9IDAuNSx2anVzdCA9IDAuNSkpKw0KICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBJc0JvcnJvd2VySG9tZW93bmVyKSkrDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxMDAwLCA1MDAwKSkNCmBgYA0KRm9yIG1vbmV5IGxlc3MgdGhhbiA1MDAwIHRoZSBtYWpvcml0eSBvZiB0aGUgYm9ycm93ZXJzIGFyZSBub3QgaG9tZSBvd25lcnMuIEFsdGhvdWdoIGluIHNvbWUgb2NjdXBhdGlvbmFsIGdyb3VwcyB0aGVyZSBhcmUgYSBsb3Qgb2YgaG9tZSBvd25lcnMuIFRoaXMgbWF5IGJlIGNhdXNlZCBieSBvdXIgb2NjdXBhdGlvbmFsIGdyb3VwaW5nLCB3aGljaCBjb250YWlucyBwcm9mZXNzaW9ucyB3aXRoIGEgd2lkZSBpbmNvbWUgcmFuZ2UuIEZvciBleGFtcGxlIGluIHRoZSBncm91cCAnTWVkaWNhbF9IZWFsdGgnLCB0aGVyZSBhcmUgZG9jdG9ycyBhbmQgbnVyc2VzIGV0Yy4NCg0KIyMjUHJvc3BlciByYXRpbmcNCmBgYHtyIHByb3NwZXIgcmF0aW5nfQ0KIy0tLS0tLS0tLS0tLS0tLS0tY3JlYXRlIGEgbmV3IERGIHdpdGggaW5mbyBhYm91dCB0aGUgcHJvc3BlciByYXRpbmcgYW5kIHRoZSBhbW91bnQNCg0KbGlicmFyeShkcGx5cikNCmNyZWRpdHNCeUdyYWRlIDwtIGdyb3VwX2J5KGxvYW5zLCBQcm9zcGVyUmF0aW5nLi5udW1lcmljLikNCmNyZWRpdHNCeUdyYWRlIDwtIHN1bW1hcmlzZShjcmVkaXRzQnlHcmFkZSwNCiAgICBtZWFuX2Ftb3VudCA9IG1lYW4oTG9hbk9yaWdpbmFsQW1vdW50KSwgDQogICAgbWVkaWFuX2Ftb3VudCA9IG1lZGlhbihMb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgIG1pbl9hbW91bnQgPSBtaW4oTG9hbk9yaWdpbmFsQW1vdW50KSwNCiAgICBtYXhfYW1vdW50ID0gbWF4KExvYW5PcmlnaW5hbEFtb3VudCksDQogICAgbiA9IG4oKSkgDQoNCmNyZWRpdHNCeUdyYWRlDQpgYGANCg0KIyMjQm9ycm93ZXIgUmF0ZSAtIFByb3NwZXIgU2NvcmUNCmBgYHtyIDEwIC0gYm9ycm93ZXIgcmF0ZSAtIHByb3NwZXIgc2NvcmV9DQpnZ3Bsb3QoYWVzKHg9Qm9ycm93ZXJSYXRlLCB5PVByb3NwZXJTY29yZSksIGRhdGEgPSBsb2FucykrDQogIGdlb21fbGluZSgpKw0KICBnZW9tX3Ntb290aCgpKw0KICBnZ3RpdGxlKCJMaW5lIFBsb3Qgb2YgYm9ycm93ZXIgcmF0ZSBhbmQgcHJvc3BlciBzY29yZSIpDQpgYGANCk5vIHN1cnByaXNlcyBoZXJlLCB0aGUgYmV0dGVyIHRoZSBQcm9zcGVyIFNjb3JlLCB0aGUgYmV0dGVyIHRoZSBib3Jyb3dlciByYXRlLg0KDQojIyNQcm9zcGVyIHJhdGluZyBpZiB0aGUgaW5jb21lIGlzIHZlcmlmaWFibGUNCldlIHN1Z2dlc3QgdGhhdCB0aGUgcHJvc3BlciByYXRpbmcgaXMgYmV0dGVyIGlmIHRoZSBpbmNvbWUgaXMgdmVyaWZpYWJsZS4NCmBgYHtyIDExIC0gUHJvc3BlciByYXRpbmcgaW4gcGVyY2VudCBieSB2ZXJpZmlhYmxlIGluY29tZX0NCmdncGxvdChhZXMoeCA9IFByb3NwZXJSYXRpbmcuLm51bWVyaWMuLCB5ID0gLi5jb3VudC4uL3N1bSguLmNvdW50Li4pKSwgZGF0YSA9IHN1YnNldChsb2FucywgIWlzLm5hKEluY29tZVZlcmlmaWFibGUpKSkgKw0KICBnZW9tX2ZyZXFwb2x5KGFlcyhjb2xvcj1JbmNvbWVWZXJpZmlhYmxlKSkgKyANCiAgeGxhYignUHJvc3BlciBSYXRpbmcnKSArIA0KICB5bGFiKCdQZXJjZW50YWdlIG9mIEJvcnJvd2VycyB3aXRoIHRoYXQgUHJvc3BlciBSYXRpbmcnKSsNCiAgZ2d0aXRsZSgiUHJvc3BlciByYXRpbmcgaW4gcGVyY2VudCBieSB2ZXJpZmlhYmxlIGluY29tZSIpDQpgYGANClllcywgb3VyIHN1Z2dlc3Rpb24gaXMgcmlnaHQuDQoNCiMjI0VtcGxveW1lbnQgc3RhdHVzIGR1cmF0aW9uDQpOZXh0IHdlIHdhbnQgdG8gdGFrZSBhIGxvb2sgYXQgdGhlIGNyZWRpdCBncmFkZSBhbmQgdGhlIGVtcGxveW1lbnQgc3RhdHVzIGR1cmF0aW9uLiBXZSBzdWdnZXN0IHRoZSBsb25nZXIgdGhlIGVtcGxveW1lbnQgc3RhdHVzLCB0aGUgYmV0dGVyIGlzIHRoZSBjcmVkaXQgZ3JhZGUuDQpgYGB7ciAxMiAtIGNyZWRpdCBncmFkZSBieSBlbXBsb3ltZW50IHN0YXR1cyBkdXJhdGlvbn0NCg0Kc3VtbWFyeShsb2FucyRFbXBsb3ltZW50U3RhdHVzRHVyYXRpb24pDQoNCmdncGxvdChhZXMoeCA9IENyZWRpdEdyYWRlLCB5ID0gRW1wbG95bWVudFN0YXR1c0R1cmF0aW9uKSwNCiAgICAgICBkYXRhID0gbG9hbnMpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSAgDQogICAgICAgICAgICAgICAgICBFbXBsb3ltZW50U3RhdHVzKSkrDQogIGdndGl0bGUoIkNyZWRpdCBncmFkZSBieSBlbXBsb3ltZW50IHN0YXR1cyBkdXJhdGlvbiIpDQpgYGANCkFzIHdlIGNhbiBzZWUgaW4gdGhlIHBsb3QgYWJvdmUsIG1vc3Qgb2YgdGhlIGxvYW4gdGFrZXJzIGFyZSBmdWxsLXRpbWUgZW1wbG95ZWVzLiBCdXQgYWxzbyB0aGVyZSBpcyBhIGxvdCBvZiBEYXRhIG1pc3NpbmcuDQpUbyBnZXQgYSBjbGVhcmVyIHBpY3R1cmUsIHdlIHdhbnQgZG8gc2F2ZSB0aGUgZW1wbG95bWVudCBzdGF0dXMgZHVyYXRpb24gaW50byBidWNrZXRzLiANCg0KYGBge3IgMTMgLSBjcmVkaXQgZ3JhZGUgYnkgZW1wbG95bWVudCBzdGF0dXMgZHVyYXRpb24gaW4gYnVja2V0c30NCg0KIyBzYXZlIGVtcGxveW1lbnQgc3RhdHVzIGR1cmF0aW9uIGludG8gYnVja2V0cy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpsb2FucyRidWNrZXRfRW1wbG95bWVudFN0YXR1c0R1cmF0aW9uIDwtIGN1dChsb2FucyRFbXBsb3ltZW50U3RhdHVzRHVyYXRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygwLCAxMiwgMjQsIDM2LCA0OCwgNTAsIDYyLCA3NCwgODYsIDk4LCAxMTAsIDEyMiwgMTM0LCAxNDYsIDc1NSkpDQoNCmdncGxvdChhZXMoeCA9IENyZWRpdEdyYWRlLCB5ID0gYnVja2V0X0VtcGxveW1lbnRTdGF0dXNEdXJhdGlvbiksIGRhdGEgPSBsb2FucykgKyANCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBFbXBsb3ltZW50U3RhdHVzKSkrDQogIGdndGl0bGUoIkNyZWRpdCBncmFkZSBieSBlbXBsb3ltZW50IHN0YXR1cyBkdXJhdGlvbiBpbiBidWNrZXRzIikNCmBgYA0KVGhpcyBwbG90IGlzIG5vdCBtdWNoIGJldHRlciwgYWx0b3VnaCBpdCBpcyBlYXNpZXIgdG8gcmVhZC4gV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGRhdGEgbWlzc2luZyBhbmQgdGhhdCBtb3N0IG9mIHRoZSBsb2FuIHRha2VycyBhcmUgZnVsbCB0aW1lIGVtcGxveWVlcy4NCg0KYGBge3IgMTQgLSBoaXN0b2dyYW0gbG9hbkFtb3VudHN9DQpnZ3Bsb3QoYWVzKHggPSBMb2FuT3JpZ2luYWxBbW91bnQsIGZpbGwgPSBMb2FuU3RhdHVzKSwgZGF0YSA9IGxvYW5zKSArDQogICAgZmFjZXRfd3JhcCh+VGVybSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGFlcyhjb2xvciA9IExvYW5TdGF0dXMpKSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZSA9ICdxdWFsJykgKw0KICAgIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBsb2FuIEFtb3VudHMgYnkgc3RhdHVzIGFuZCB0ZXJtcyIpDQpgYGANCkluIHRoZSBwbG90IGFib3ZlIHdlIGNhbiBzZWUgdGhhdCBtb3N0IG9mIHRoZSBsb2FucyBhcmUgbWlkLXRlcm0uIA0KDQojIyNEZWJ0IHRvIGluY29tZSByYXRpbw0KYGBge3IgMTUgLSBEZXB0IHRvIGluY29tZSByYXRpb30NCmdncGxvdChkYXRhID0gbG9hbnMsIGFlcyh4ID0gRGVidFRvSW5jb21lUmF0aW8pKSArICAgICAgICAgICAgICAgIA0KICAgICAgICBnZW9tX2hpc3RvZ3JhbShjb2xvdXIgPSAnIzI0MzIzZScsIGZpbGwgPSAnIzAyY2NiYScsIGJpbndpZHRoID0gMC4wMDUpICsNCiAgICAgICAgeGxpbSgwLCBxdWFudGlsZShsb2FucyREZWJ0VG9JbmNvbWVSYXRpbywgcHJvYiA9IDAuNSwgbmEucm0gPSBUUlVFKSkgKw0KICAgICAgICBnZ3RpdGxlKCJEZWJ0IFRvIEluY29tZSBSYXRpbyIpICsNCiAgICAgICAgeGxhYigiRGVidCB0byBJbmNvbWUgUmF0aW8iKSArDQogICAgICAgIHlsYWIoIkNvdW50IikNCiAgICAgICAgc3VtbWFyeShsb2FucyRsb2FuX2luY29tZV9yYXRpbykNCmBgYCANCiMjI0xlbmRlciB5aWVsZA0KV2Ugc3VnZ2VzdCB0aGF0IHRoZXJlIGlzIGEgaGlnaGVyIGxlbmRlciB5aWVsZCwgaWYgdGhlIGJvcnJvd2VyIHJhdGUgaXMgaGlnaGVyLg0KYGBge3IgMTYgLSBsZW5kZXIgeWllbGQgYW5kIGJvcnJvd2VyIHJhdGV9DQpsaWJyYXJ5KGRwbHlyKQ0KZ2dwbG90KGxvYW5zLCBhZXMoeCA9IExlbmRlcllpZWxkLCB5ID0gQm9ycm93ZXJSYXRlKSkgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMS8yMCwgY29sb3VyID0gJyMwMmNjYmEnKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIHF1YW50aWxlKGxvYW5zJExlbmRlcllpZWxkLCAwLjc1KSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKCANCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCAsIHF1YW50aWxlKGxvYW5zJEJvcnJvd2VyUmF0ZSwgMC43NSkpKSArDQogIGdndGl0bGUoJ0xlbmRlciB5aWVsZCBhbmQgYm9ycm93ZXIgcmF0ZScpDQpgYGANClllcywgd2UgY2FuIHNlZSBjbGVhcmx5IHRoZSBoaWdoZXIgdGhlIGJvcnJvd2VyIHJhdGUsIHRoZSBoaWdoZXIgdGhlIGxlbmRlciB5aWVsZC4NCg0KIyMjTGVuZGVyIHlpZWxkIGJ5IG51bWJlciBvZiBJbnZlc3RvcnMNCmBgYHtyIDE3IC0gTGVuZGVyIFlpZWxkIGJ5IG51bWJlciBvZiBJbnZlc3RvcnN9DQpnZ3Bsb3QoYWVzKHggPSBCb3Jyb3dlclJhdGUsIHkgPSBJbnZlc3RvcnMpLCBkYXRhPWxvYW5zKSArIA0KICBnZW9tX3BvaW50KGFlcyhjb2xvcj1MZW5kZXJZaWVsZCkpKw0KICB5bGltKDAsIDYwMCkrDQogIGdndGl0bGUoJ0xlbmRlciB5aWVsZCBieSBudW1iZXIgb2YgaW52ZXN0b3JzJykNCmBgYA0KSW4gdGhlIHBsb3QgYWJvdmUgd2UgY2FuIHNlZSBxdWl0ZSB3ZWxsLCB0aGF0IHRoZSBpbnZlc3RvcnMgeWllbGQgZ2V0cyBoaWdoZXIgdGhlIGhpZ2hlciB0aGUgYm9ycm93ZXIgcmF0ZSBnZXRzLg0KDQpgYGB7ciAxOCAtIE51bWJlciBvZiBJbnZlc3RvcnMgcGVyIExvYW59DQpnZ3Bsb3QoYWVzKHggPSBsb2FucyRJbnZlc3RvcnMsIHkgPSAuLmNvdW50Li4pLCBkYXRhID0gbG9hbnMpICsNCiAgZ2VvbV9mcmVxcG9seShhZXMoY29sb3IgPSBJbnZlc3RvcnMpLCBiaW53aWR0aD0wLjEpICsgDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI1MCksIGJyZWFrcyA9IHNlcSgwLCA1MCwgMjUwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDc1MCwgNTApKSsNCiAgeWxpbSgwLCA3NTApKw0KICB4bGltKDEsIDI1MCkrDQogIHhsYWIoJ251bWJlciBvZiBJbnZlc3RvcnMnKSArIA0KICB5bGFiKCdjb3VudCcpICsNCiAgZ2d0aXRsZSgnTnVtYmVyIG9mIEludmVzdG9ycyBwZXIgTG9hbicpDQpgYGANClRoZSBtYWpvcml0eSBvZiB0aGUgSW52ZXN0b3JzIGlzIGp1c3Qgb25lIHBlcnNvbi4gSW4gdGhlIHBsb3QgYWJvdmUsIHdlIGxpbWl0IHRoZSBudW1iZXIgb2YgbG9hbnMgZ2l2ZW4gdG8gMTUwMCBpbiBvcmRlciB0byBnZXQgYSBiZXR0ZXIgcGljdHVyZSBvZiBsb2FucyBnaXZlbiBieSBtb3JlIHRoZW4gb25lIGludmVzdG9yLiAgDQoNCiMjI0N1cnJlbnQgZGVsaW5xdWVuY2llcyBieSBjcmVkaXQgZ3JhZGUNCmBgYHtyIDE5IC0gQ3VycmVudCBEZWxpbnF1ZW5jaWVzIGJ5IGNyZWRpdCBncmFkZX0NCiAgICBxcGxvdCh4PUNyZWRpdEdyYWRlLCB5PURlbGlucXVlbmNpZXNMYXN0N1llYXJzLA0KICAgICAgICBkYXRhPWxvYW5zLCBnZW9tPSdib3hwbG90JykrDQogICAgeWxpbSgwLCAyNSkNCmBgYA0KQXMgd2UgY2FuIHNlZSBpbiB0aGUgcGxvdCBhYm92ZSwgdGhlIGJldHRlciB0aGUgY3JlZGl0IGdyYWRlLCBsZXNzIGRlbGlucXVlbmljZXMuDQoNCiMjI0xvYW4gYW1vdW50IGJ5IHRlcm0sIGdyb3VwZWQgYnkgZ3JvdXBlZCBvY2N1cGF0aW9uIGFuZCBjYXRlZ29yeQ0KYGBge3IgMjAgLSBsb2FuIGFtb3VudCBieSB0ZXJtLCBncm91cGVkIGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgY2F0ZWdvcnl9DQpnZ3Bsb3QoYWVzKHggPSBUZXJtLCB5ID0gTG9hbk9yaWdpbmFsQW1vdW50KSwgZGF0YSA9IGxvYW5zKSArDQogICAgZmFjZXRfd3JhcCh+R3JvdXBlZE9jY3VwYXRpb24pICsNCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENhdGVnb3J5KSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDEwMDAsIDUwMDApKQ0KYGBgDQpUaGUgcGxvdCBhYm92ZSBnaXZlcyB1cyBhIG5pY2Ugb3ZlcnZpZXcuIEFzIHdlIGNhbiBzZWUgbW9zdCBvZiB0aGUgbG9hbnMgYXJlIG1pZCB0ZXJtIGxvYW5zIHdpdGggYSBkdXJhdGlvbiBvZiAzNiBtb250aC4gVGhlIHVzYWdlcyBvZiB0aGUgbG9hbnMgYXJlIHdlbGwgbWl4ZWQuIA0KDQoNCmBgYHtyIDIxIC0gRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIGFuZCBvY2N1cGF0aW9uIG9mIGJvcnJvd2VyfQ0KZ2dwbG90KGFlcyhFc3RpbWF0ZWRSZXR1cm4sIEVzdGltYXRlZExvc3MpLCANCiAgICAgICBkYXRhPXN1YnNldChsb2FucywgR3JvdXBlZE9jY3VwYXRpb24gIT0gJ090aGVyJyANCiAgICAgICAgICAgICAgICAgICAmICFpcy5uYShHcm91cGVkT2NjdXBhdGlvbikNCiAgICAgICAgICAgICAgICAgICAmICFpcy5uYShJbmNvbWVSYW5nZSkNCiAgICAgICAgICAgICAgICAgICAmIEluY29tZVJhbmdlICE9ICdOb3QgZGlzcGxheWVkJw0KICAgICAgICAgICAgICAgICAgJiBJbmNvbWVSYW5nZSAhPSAnTm90IGVtcGxveWVkJykpKw0KICBnZW9tX3BvaW50KGFlcyhzaXplPUluY29tZVJhbmdlLCBjb2xvdXI9R3JvdXBlZE9jY3VwYXRpb24pKSsNCiAgZ2d0aXRsZSgnRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIG9mIGJvcnJvd2VyJykNCmBgYA0KVGhlIHBsb3QgYWJvdmUgZ2l2ZXMgYW4gb3ZlcnZpZXcgb2YgdGhlIGVzdGltYXRlZCByZXR1cm4gYW5kIGVzdGltYXRlZCBsb3NzIGJ5IGdyb3VwZWQgb2NjdXBhdGlvbiBhbmQgaW5jb21lIHJhbmdlLg0KV2UgY2FuIHNlZSB0aGF0IHRoZXJlIGlzIGEgc21hbGwgZ3JvdXAgd2hlcmUgdGhlIGVzdGltYXRlZCBsb3NzIGlzIGhpZ2ggYW5kIHRoZXJlIGlzIG5vdCBhbiBlc3RpbWF0ZWQgcmV0dXJuLiBCdXQgd2UgY2FuJ3Qgc2VlIHRoYXQgdGhpcyBpcyBoYXBwZW5pbmcgb25seSB0byBhIHNwZWNpYWwgb2NjdXB0aW9uIGdyb3VwIG9yIGluY29tZSByYW5nZSBpbiB0aGVyZS4gTW9zdCBvZiB0aGUgZXN0aW1hdGVkIHJldHVybnMgYW5kIGVzdGltYXRlZCBsb3NzZXMgYXJlIGJldHdlZW4gMCBhbmQgMC4xLg0KDQpCZWNhdXNlIHRoZSBwbG90IGlzIGhhcmQgdG8gcmVhZCBkdWUgdG8gdGhlIGRlbnNpdHkgb2YgaW5mb3JtYXRpb24sIHdlIHdpbGwgdGFrZSBhIGNsb3NlciBsb29rIGF0IG9uIG9jY3VwYXRpb24gZ3JvdXAgLSB0aGUgc3R1ZGVudHMuDQoNCmBgYHtyIDIyIC0gRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIGZvciBzdHVkZW50c30NCmdncGxvdChhZXMoRXN0aW1hdGVkUmV0dXJuLCBFc3RpbWF0ZWRMb3NzKSwgDQogICAgICAgZGF0YT1zdWJzZXQobG9hbnMsIEdyb3VwZWRPY2N1cGF0aW9uID09ICdTdHVkZW50JyANCiAgICAgICAgICAgICAgICAgICAmIEluY29tZVJhbmdlICE9ICdOb3QgZGlzcGxheWVkJw0KICAgICAgICAgICAgICAgICAgJiBJbmNvbWVSYW5nZSAhPSAnTm90IGVtcGxveWVkJykpKw0KICBnZW9tX3BvaW50KGFlcyhzaXplPUluY29tZVJhbmdlLCBjb2xvdXI9SW5jb21lUmFuZ2UpKSsNCiAgZ2d0aXRsZSgnRXN0aW1hdGVkIGxvc3MgYW5kIGVzdGltYXRlZCByZXR1cm4gYnkgaW5jb21lIHJhbmdlIGZvciBzdHVkZW50cycpDQpgYGANClRoaXMgcGxvdCBzaG93cyB0aGF0IHRoZXJlIGFyZSBzb21lIHN0dWRlbnRzIHRoYXQgc2VlbSB0byBlYXJuIGFscmVhZHkgYSBsb3Qgb2YgbW9uZXksIGFsdG91Z2h0IHRoZSBtYWpvcml0eSBlYXJucyBiZXR3ZWVuICQxIC0gMjQ5OTkuDQoNCmBgYHtyIDIzIC0gU2VydmljZSBmZWVzIGFuZCBpbnRlcmVzdGFuZCBmZWVzIGR1ZSB0byBjdXN0b21lciBwYXltZW50c30NCmdncGxvdChsb2FucywgYWVzKExQX0N1c3RvbWVyUGF5bWVudHMsIExQX0ludGVyZXN0YW5kRmVlcykpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyID1MUF9TZXJ2aWNlRmVlcyksIHNpemUgPSAxKSArDQogIGNvb3JkX2VxdWFsKCkNCmBgYA0KVGhpcyBwbG90IHNob3dzIHRoYXQgdGhlIGxvd2VyIHRoZSBjdXN0b21lciBwYXltZW50cyBhcmUgdGhlIGxvd2VyIHRoZSBzZXJ2aWNlIGZlZXMgYW5kIGludGVyZXN0IGZlZXMgYXJlLg0KDQojIyNIZWF0bWFwDQpgYGB7ciAyNCAtIFNpbXBsZSBIZWF0bWFwIG9mIExvYW5zIGJ5IG9jY3VwYXRpb24gYW5kIGNhdGVnb3J5IH0NCmdncGxvdChkYXRhID0gbG9hbnMsIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBHcm91cGVkT2NjdXBhdGlvbikpICsNCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gTG9hbk9yaWdpbmFsQW1vdW50KSkgKw0KICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NjAsaGp1c3Q9MSx2anVzdD0wLjkpKQ0KYGBgDQpJbiB0aGUgcGxvdCBhYm92ZSB3ZSBnZXQgYSBuaWNlIG92ZXJ2aWV3IG9mIHRoZSBjYXRlZ29yeSBvZiBsb2FucyBhbmQgdGhlIGdyb3VwZWQgb2NjdXBhdGlvbnMuIA0KDQojI1N1bW1hcnkNCkJlY2F1c2Ugb2YgdGhlIGJpZyBhbW91bnQgb2YgdmFyaWFibGVzIGl0IHRvb2sgc29tZSB0aW1lLCB0byByZWFkIHRocm91Z2ggdGhlIGV4cGxhbmF0aW9ucyBvZiB0aGUgcHJvc3BlciBsb2FuIGRhdGEuIFRvIGdldCBzdGFydGVkIHdlIGV4cGxvcmVkIHNvbWUgZGlmZmVyZW50IHZhcmlhYmxlcy4gDQpJbiBvcmRlciB0byBnZXQgbmljZSBwbG90cywgd2UgaGFkIHRvIGNvbnZlcnQgc29tZSB2YWx1ZXMuIEZvciBleGFtcGxlIHRoZSBvcmlnaW4gZGF0ZSB0byB5ZWFyLCB0aGUgbnVtZXJpYyBjYXRlZ29yaWVzIGludG8gcmVhZGFibGUgY2F0ZWdvcmllcyBhbmQgdGhlIGpvYiBkdXJhdGlvbiBtb250aHMgd2hlcmUgc3VtbWFyaXplZCBpbiBidWNrZXRzDQpJdCB3YXMgaW50ZXJyZXN0aW5nIHRvIHNlZSB0aGF0IGZyb20gMjAxMSBvbiBib3Jyb3dlcnMgbmVlZGVkIGhpZ2hlciBsb2FucyB3aXRoIGxvbmdlciB0ZXJtcy4gDQpJdCB3YXMgcXVpdGUgYSBzdXJwcmlzZSB0aGF0IHRoZXJlIGlzIG5vIGJpZyBkaWZmZXJlbmNlIGJldHdlZW4gaG9tZSBvd25lcnMgYW5kIG5vbiBob21lIG93bmVycywgYmVjYXVzZSB3ZSBzdWdnZXN0ZWQgdGhhdCBob21lIG93bmVycyBhcmUgZmluYW5jaWFsbHkgbW9yZSBzdHJvbmcgYW5kIGRvbid0IG5lZWQgc21hbGwgbG9hbnMuIA0KRm9yIHRoZSBvdGhlciB2YXJpYWJsZXMgSSBjb3VsZCBub3QgZmluZCBhIGxvdCBvZiBzdXJwcmlzaW5nIGZhY3RzLiBGb3IgZXhhbXBsZSB0aGUgd29yc2UgdGhlIGNyZWRpdCBncmFkZSBpcywgdGhlIGhpZ2hlciB0aGUgZGVsaW5xdWVuY2llcyBpbiB0aGUgbGFzdCA3IHllYXJzIGFyZSBvciB0aGF0IHRoZSBsZW5kZXIgeWllbGQgZ2V0cyBsb3dlciB0aGUgaGlnaGVyIHRoZSBudW1iZSBvZiBpbnZlc3RvcnMgZ2V0Lg0KSXQgd291bGQgYmUgbmljZSBpZiB0aGVyZSB3ZXJlIG5vdCBiaWcgZ3JvdXBzIGxpa2UgJ25hJyBvciAnb3RoZXInIGluIHRoZSBvY2N1cGF0aW9uIGFuZCBjYXRlZ29yeSBncm91cC4gTWF5YmUgdGhlcmUgY291bGQgYmUgYWxzbyBkYXRhIGFib3V0IHRoZSBhZ2UgYW5kIGdlbmRlciBvZiB0aGUgYm9ycm93ZXIgcHJvdmlkZWQsIHdoaWNoIG1heSBsZWFkIHRvIGludGVyZXN0aW5nIGZpbmRpbmdzLg0K